package com.siebre.picclife.asynctask.springcontext;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;

import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.siebre.picclife.asynctask.TaskException;
import com.siebre.picclife.asynctask.service.support.TaskHandler;
import com.siebre.picclife.asynctask.service.support.TaskHandlerRegistrar;
import com.siebre.picclife.asynctask.service.support.TaskHandlerRegistry;
import com.siebre.picclife.asynctask.utils.TaskMappingUtils;

/**
 * 
 * @author ZhangChi
 * @since 2015年4月13日
 *
 */
public class AnnotatedTaskHandlerRegistrar implements BeanPostProcessor, TaskHandlerRegistrar {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(AnnotatedTaskHandlerRegistrar.class);
	
	private Set<Class<?>> taskServiceInterfaces = Sets.newHashSet();
	
	private List<TaskHandlerRegistrar> registrars = Lists.newArrayList();
	
	private List<TaskHandlerRegistry> registries = Lists.newArrayList();

	public AnnotatedTaskHandlerRegistrar(String packageName) {
		TaskProxyScanner scanner = new TaskProxyScanner();
		Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(packageName);
		LOGGER.debug("found {} task proxy interfaces", candidateComponents.size());
		for (BeanDefinition bd : candidateComponents) {
			ScannedGenericBeanDefinition sbd = (ScannedGenericBeanDefinition) bd;
			try {
				sbd.resolveBeanClass(getClass().getClassLoader());
				final Class<?> serviceInterface = sbd.getBeanClass();
				taskServiceInterfaces.add(serviceInterface);
			} catch (Exception e) {
				throw new TaskException("failed to scan task proxy service " + sbd.getBeanClass(), e);
			}
		}
	}

	public Object postProcessBeforeInitialization(final Object bean, String beanName)
			throws BeansException {
		final Class<?> serviceInterface = FluentIterable.from(taskServiceInterfaces)
				.firstMatch(new Predicate<Class<?>>() {
					
					public boolean apply(Class<?> input) {
						return input.isInstance(bean);
					}
				}).orNull();
		if (serviceInterface != null) {
			
			LOGGER.debug("create TaskHandlerRegistrar for task service: {}", bean);
			
			TaskHandlerRegistrar registrar = new TaskHandlerRegistrar() {
				
				public void register(TaskHandlerRegistry registry) {
					for (Method method : serviceInterface.getDeclaredMethods()) {
						String taskType = TaskMappingUtils.resolveTaskType(method);
						registry.register(taskType, new TaskHandler(bean, method));
					}
				}

				@Override
				public String toString() {
					return "registrar for " + serviceInterface;
				}
			};
			for (TaskHandlerRegistry registry : registries) {
				doRegister(registry, registrar);
			}
			registrars.add(registrar);
		}
		return bean;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	public void register(TaskHandlerRegistry registry) {
		for (TaskHandlerRegistrar registrar : registrars) {
			doRegister(registry, registrar);
		}
		registries.add(registry);
	}

	private void doRegister(TaskHandlerRegistry registry, TaskHandlerRegistrar registrar) {
		LOGGER.debug("register task handler, registry={}, registrar={}", registry.getClass().getName(), registrar);
		registrar.register(registry);
	}

}
